package com.acmedcare.framework.exchange.center.core;

import com.acmedcare.framework.exchange.center.common.ConvertUtils;
import com.acmedcare.framework.exchange.center.core.adapter.AdapterMetadata;
import com.acmedcare.framework.exchange.center.core.adapter.IAdapterResolver;
import com.acmedcare.framework.exchange.center.core.agent.AgentMetadata;
import com.acmedcare.framework.exchange.center.core.agent.IAgentResolver;
import com.acmedcare.framework.exchange.center.core.application.IApplicationResolver;
import com.acmedcare.framework.exchange.center.core.consumer.ConsumerMetadata;
import com.acmedcare.framework.exchange.center.core.consumer.IConsumerResolver;
import com.acmedcare.framework.exchange.center.core.exception.PluginNotFoundException;
import com.acmedcare.framework.exchange.center.core.json.IPluginExtensionConvert;
import com.acmedcare.framework.exchange.center.entity.bean.Plugin;
import com.acmedcare.framework.exchange.center.entity.bean.PluginStatusEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@SuppressWarnings("unused")
public class PluginContainer implements PluginContainerMBean {

  private ConcurrentHashMap<String, PluginMetadata> pluginMetadata;
  //private ThreadPoolExecutor threadPoolExecutor;
  private boolean loaded = false;
  private final IAdapterResolver adapterResolver;
  private final IPluginExtensionConvert pluginExtensionConvert;
  final IAgentResolver agentResolver;
  final IApplicationResolver applicationResolver;
  final IConsumerResolver consumerResolver;
  final ObjectMapper objectMapper;

  public PluginContainer(IAdapterResolver adapterResolver,
      IPluginExtensionConvert pluginExtensionConvert,
      IAgentResolver agentResolver,
      IApplicationResolver applicationResolver,
      IConsumerResolver consumerResolver, ObjectMapper objectMapper) {
    this.adapterResolver = adapterResolver;
    this.pluginExtensionConvert = pluginExtensionConvert;
    this.agentResolver = agentResolver;
    this.applicationResolver = applicationResolver;
    this.consumerResolver = consumerResolver;
    this.objectMapper = objectMapper;
    pluginMetadata = new ConcurrentHashMap();

    //使用线程池并行加载执行类，提高执行效率，同时控制同时加载的文件的数量
    // threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10,
    //    TimeUnit.SECONDS,
    //  new ArrayBlockingQueue<>(100));
  }

  public void init(List<Plugin> plugins) {

    if (plugins == null) {

      return;
    }

    if (loaded) {

      return;
    }

    loaded = true;
    if (plugins.isEmpty()) {

      return;
    }

    boolean isFull = false;
    for (Plugin p : plugins) {

      p.setStatus(PluginStatusEnum.UNLOAD);
     /* try {

        if (!isFull) {

          threadPoolExecutor.submit(() -> resolvePlugin(p));
        } else {

          resolvePlugin(p);
        }
      } catch (RejectedExecutionException rejectedExecutionException) {
        isFull = true;
      }*/
      resolvePlugin(p);
    }
  }

  private void unload(Plugin plugin) {

    plugin.setName(plugin.getName().toLowerCase());
    if (!loaded) {

      log.info("插件容器尚未初始化，无法执行卸载！");
      return;
    }

    log.info(String.format("正在卸载插件，插件名称：%s,插件版本：%s", plugin.getName(), plugin.getVersion()));
    PluginMetadata metadata = pluginMetadata.get(plugin.getName());
    if (metadata != null) {
      try {

        synchronized (metadata) {

          if (metadata.getStatus().equals(PluginStatusEnum.UNLOAD)) {

            return;
          }
        }

        if (metadata.getApplicationInstance() != null) {

          new Thread(() -> metadata.getApplicationInstance().close()).start();
        }

        if (metadata.getClassLoader() instanceof URLClassLoader) {

          ((URLClassLoader) metadata.getClassLoader()).close();
        }
      } catch (Exception e) {

        log.error(String.format("插件：%s，版本：%s 卸载失败！", plugin.getName(), plugin.getVersion()), e);
      } finally {

        metadata.setStatus(PluginStatusEnum.UNLOAD);
        pluginMetadata.remove(plugin.getName());
      }

      log.info(String.format("插件：%s，版本：%s 卸载完成", plugin.getName(), plugin.getVersion()));
    } else {

      log.warn(
          String.format("插件：%s，版本：%s 在当前容器中不存在，无法完成卸载！", plugin.getName(), plugin.getVersion()));
    }
  }


  public void unloadAsync(Plugin plugin) {

    // threadPoolExecutor.submit(() -> unload(plugin));
    unload(plugin);
  }

  public void load(Plugin plugin) {

    plugin.setName(plugin.getName().toLowerCase());
    if (!loaded) {

      log.info("插件尚未完成初始化,正在执行初始化！");
      List<Plugin> plugins = new ArrayList<>();
      plugins.add(plugin);
      init(plugins);
      return;
    }

    resolvePlugin(plugin);
  }

  public PluginMetadata getPlugin(String pluginName) throws PluginNotFoundException {

    PluginMetadata metadata = pluginMetadata.get(pluginName.toLowerCase());
    if (metadata == null) {

      throw new PluginNotFoundException(String.format("插件:%s尚未加载成功,请确认是否存在此插件!", pluginName));
    }

    return metadata;
  }


  public List<PluginMetadata> getPlugins() {

    List<PluginMetadata> plugins = new ArrayList<>();

    pluginMetadata.forEach((k, v) -> plugins.add(v));

    return plugins;
  }

  @Override
  public void setEnv(String key, String value) {
    log.info(String
        .format("当前环境:%s,正在写入环境变量，变量名:%s,变量值：%s", System.getenv(Const.GroupName),
            key, value));
    System.setProperty(key, value);
    getPlugins().forEach(p -> {
      if (p.getApplicationInstance() != null) {
        p.getApplicationInstance()
            .configChangeNotify(key, value);
      }
    });
  }

  private synchronized void resolvePlugin(Plugin plugin) {

    plugin.setName(plugin.getName().toLowerCase());
    // plugin.setStatus(PluginStatusEnum.INIT);
    PluginMetadata metadata = pluginMetadata.get(plugin.getName());

    if (metadata != null) {

      try {

        if (metadata.getVersion().equals(plugin.getVersion()) && metadata.getStatus()
            .equals(PluginStatusEnum.LOADED)) {

          log.warn(
              String.format("容器中已经存在当前版本插件,插件名：%s,插件版本:%s", plugin.getName(), plugin.getVersion()));
          return;
        } else {

          //版本不一样，先卸载当前版本插件，然后再加载新版本插件
          unload(plugin);
        }
      } catch (Exception e) {

        log.error(String.format("插件：%s加载失败,插件版本:%s", plugin.getName(), plugin.getVersion()), e);
        return;
      }
    }

    //如果不存在直接加载新版本
    log.info(String.format("正在加载插件，插件名称：%s,插件版本：%s", plugin.getName(), plugin.getVersion()));
    ConcurrentHashMap<String, AdapterMetadata> adapterMetadata = new ConcurrentHashMap<>();
    ConcurrentHashMap<String, AgentMetadata> agentMap = new ConcurrentHashMap<>();
    ConcurrentHashMap<String, ConsumerMetadata> consumerMap = new ConcurrentHashMap<>();

    metadata = ConvertUtils.convertType(plugin, PluginMetadata.class);
    metadata.setStatus(PluginStatusEnum.UNLOAD);
    metadata.setAdapterMetadata(adapterMetadata);
    metadata.setAgentMap(agentMap);
    metadata.setConsumerMap(consumerMap);

    try {
      //  synchronized (metadata) {

        if (metadata.getStatus().equals(PluginStatusEnum.LOADED)) {

          return;
        }

        metadata.setStatus(PluginStatusEnum.LOADED);
        //解析并且初始化应用程序
        applicationResolver.resolve(metadata);
        //解析并且初始化适配器
        adapterResolver.resolve(adapterMetadata, metadata);
        //解析并初始化采集器
        agentResolver.resolve(agentMap, metadata);
        //解析消费器
        consumerResolver.resolve(consumerMap, metadata);
        pluginExtensionConvert.registerTypes(metadata);
        //释放san result
        metadata.setScanResult(null);
      //  }

      pluginMetadata.put(plugin.getName(), metadata);
      log.info(String.format("插件：%s加载成功,插件版本:%s,元数据：", plugin.getName(), plugin.getVersion(),
          objectMapper.writeValueAsString(metadata)));
    } catch (Exception e) {

      log.error(String.format("插件：%s加载失败,插件版本:%s", plugin.getName(), plugin.getVersion()), e);
      pluginMetadata.remove(plugin.getName());
    } finally {

    }
  }
}
